﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace bxml.betterxml.xir
{

    public class XIRDataObject
    {
        /**
         * Every XIR record has a type, which corresponds to the kind of XML
         * "thing" being encoded. Constants that may be used begin with the
         * XML_ prefix.
         */
        private const String XIR_TYPE = "xir.type";
        /*
         * The subtype is used to qualify a record type (often begin/end). An
         * XIR record (presently) is fully distinguished by its type and subtype
         * pair.
         */
        private const String XIR_SUBTYPE = "xir.subtype";

        /*
         * XIR records may use any desired key, except one that begins with
         * the reserved prefix "xir."
         */
        private const String XIR = "xir.";

        /*
         * XIR_VERBATIM and XIR_B64 are the only two encodings supported by 
         * the present implementation. XIR_VERBATIM means the character data is
         * encoded "as is", while XIR_B64 is used to wrap the data in Base64.
         * We use B64 whenever there is the possibility of data being encoded
         * across newline boundaries. (XIR is a line-oriented record format!)
         */

        private Dictionary<String, EncodedString> data = new Dictionary<String, EncodedString>();

        //private String subtypeString, typeString;


        public XIRDataObject(XIRDataObjectTypes type, XIRDataObjectSubtypes subtype)
        {
            data.Add(XIR_TYPE, new EncodedString(XIREncodings.verbatim, type.ToString()));
            data.Add(XIR_SUBTYPE, new EncodedString(XIREncodings.verbatim, subtype.ToString()));

            //subtypeString
        }

        public XIRDataObject(XIRDataObjectTypes type)
            : this(type, XIRDataObjectSubtypes.None)
        {

        }

        public XIRDataObject()
            : this(XIRDataObjectTypes.None, XIRDataObjectSubtypes.None)
        {

        }

        public void setType(XIRDataObjectTypes type)
        {
            data.Add(XIR_TYPE, new EncodedString(XIREncodings.verbatim, type.ToString()));

        }

        public void setSubtype(XIRDataObjectSubtypes subtype)
        {
            data.Add(XIR_SUBTYPE, new EncodedString(XIREncodings.verbatim, subtype.ToString()));
        }

        public void setVerbatim(String key, String value)// throws XIRDataObjectException 
        {
            //if (key.startsWith(XIR))
            //	throw new XIRDataObjectException("Illegal Key", key);
            data.Add(key, new EncodedString(XIREncodings.verbatim, value));
        }

        public void setBase64(String key, String value) //throws XIRDataObjectException 
        {
            //if (key.startsWith(XIR))
            //	throw new XIRDataObjectException("Illegal Key", key);
            String value64 = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(value), Base64FormattingOptions.None);

            EncodedString encodedData = new EncodedString(XIREncodings.base64, value64);
            data.Add(key, encodedData);
        }

        public XIREncodings getEncoding(String key)
        {
            return data[key].getEncoding();
        }

        public String getValue(String key)
        {
            XIREncodings encoding = data[key].getEncoding();
            if (encoding == XIREncodings.verbatim)
                return data[key].getValue();
            else
            {
                // TODO: We need to think about whether "locale" infomration should be saved when doing the b64 encoding.
                return UTF8Encoding.UTF8.GetString(Convert.FromBase64String(data[key].getValue()));
            }
        }

        public XIRDataObjectTypes getType()
        {
            String typeAsText = data[XIR_TYPE].getValue();
            return (XIRDataObjectTypes)Enum.Parse(typeof(XIRDataObjectTypes), typeAsText);
        }

        public XIRDataObjectSubtypes getSubtype()
        {
            String subtypeAsText = data[XIR_SUBTYPE].getValue();
            return (XIRDataObjectSubtypes)Enum.Parse(typeof(XIRDataObjectSubtypes), subtypeAsText);
        }

        public override String ToString()
        {
            StringBuilder buffer = new StringBuilder();
            foreach (var pair in data)
            {
                EncodedString value = pair.Value;
                buffer.Append(pair.Key);
                buffer.Append(":");
                buffer.Append(value.getEncoding());
                buffer.Append("=");
                buffer.Append(value.getValue());
                buffer.Append("\n");
            }
            return buffer.ToString();
        }

        public static XIRDataObject getXIRDataObject(List<String> record)
        {
            XIRDataObject xdo = new XIRDataObject();
            foreach (String field in record)
            {
                /*String[] fieldSpit = field.split("=");
                if(fieldSpit.length!=2)
                    break;
                String var = fieldSpit[0];
                String value = fieldSpit[1];
                    
                String[] varSplit = var.split(":");
                if(varSplit.length != 2)
                    break;
                var = varSplit[0];
                String encoding = varSplit[1];*/


                int eqPos = field.IndexOf('=');
                if (eqPos < 0)
                    break; // TODO: Add exception code here.		
                String var = field.Substring(0, eqPos);
                String value = field.Substring(eqPos + 1);
                int colonPos = var.IndexOf(':');
                if (colonPos < 0)
                    break; // TODO: Add exception code here.
                String encoding = var.Substring(colonPos + 1);
                var = var.Substring(0, colonPos);
                if (var == XIR_TYPE)
                    xdo.setType((XIRDataObjectTypes)Enum.Parse(typeof(XIRDataObjectTypes), value));
                else if (var == XIR_SUBTYPE)
                    xdo.setSubtype((XIRDataObjectSubtypes)Enum.Parse(typeof(XIRDataObjectSubtypes), value));
                else
                    xdo.data.Add(var, new EncodedString((XIREncodings)Enum.Parse(typeof(XIREncodings), value), value));
            }
            return xdo;
        }
    }

}
